#pragma once
#ifndef _KH_CORE_OBJECTBASE_H
#define _KH_CORE_OBJECTBASE_H
#include <Kharlia/Core/Common.h>
#include <Kharlia/Core/GC.h>
#include <Kharlia/Core/Ptr.h>

#define KH_DEF_GET(TYPE, NAME) inline TYPE Get##NAME() const { return m##NAME; }
#define KH_DEF_SET(TYPE, NAME) inline void Set##NAME(TYPE InValue) { m##NAME = InValue; }
#define KH_DEF_GETSET(TYPE, NAME) KH_DEF_GET(TYPE, NAME) KH_DEF_SET(TYPE, NAME)
#define KH_DEF_GET_P(TYPE, PRE, NAME) inline TYPE PRE##Get##NAME() const { return m##PRE##NAME; }
#define KH_DEF_SET_P(TYPE, PRE, NAME) inline void PRE##Set##NAME(TYPE InValue) { m##PRE##NAME = InValue; }
#define KH_DEF_GETSET_P(TYPE, PRE, NAME) KH_DEF_GET_P(TYPE, PRE, NAME) KH_DEF_SET_P(TYPE, PRE, NAME)

/// Specifies that a type exposed using Boost.Python takes a back reference
/// (pointer to Python object self) as the first argument to constructor.
#define ENABLE_BACK_REFERENCE(TYPE) \
    namespace boost { namespace python { template <> struct has_back_reference<TYPE>: mpl::true_ {};}}

/// Declare a Boost.Python pickling suite named 'type##Pickler' for the specified type.
#define DECL_PICKLER(api, type, statetype) \
    struct api type##Pickler: public ::boost::python::pickle_suite { \
        static ::boost::python::tuple getinitargs(type& self); \
        static statetype getstate(type& self); \
        static void setstate(type& self, statetype state); \
        static bool getstate_manages_dict() { return true; } \
    }

/// Declare a nested pickling suite
#define DECL_PICKLER_N(type, statetype) \
    struct Pickler: public ::boost::python::pickle_suite { \
        static ::boost::python::tuple getinitargs(type& self); \
        static statetype getstate(type& self); \
        static void setstate(type& self, statetype state); \
        static bool getstate_manages_dict() { return true; } \
    }

/// Declare a Boost.Python pickling suite that only includes getinitargs().
#define DECL_PICKLER_NOSTATE(api, type) \
    struct api type##Pickler: public ::boost::python::pickle_suite { \
        static ::boost::python::tuple getinitargs(type& self); \
        static bool getstate_manages_dict() { return true; } \
    }

#define DECL_PICKLER_NOSTATE_N(type) \
    struct Pickler: public ::boost::python::pickle_suite { \
        static ::boost::python::tuple getinitargs(type& self); \
        static bool getstate_manages_dict() { return true; } \
    }

#define DECL_PICKLER_NOINIT_N(type, statetype) \
    struct Pickler: public ::boost::python::pickle_suite { \
        static bool getstate_manages_dict() { return true; } \
        static statetype getstate(type& self); \
        static void setstate(type& self, statetype state); \
    }

namespace kharlia { namespace core {

void _InitObjectBindings();

/// This is the base for all native classes that are subclassed in Python
/// All instances must be created from Python and held by a shared_ptr
/// Some const versions of functions are included here for compatibility
class KH_CORE_API CObject: public boost::noncopyable {
    KH_DECL_GC()

protected:
    PyObject* mSelf;
    
public:
    CObject(PyObject* self);
    virtual ~CObject();

    object GetSelf() const {
        return object(handle<>(borrowed(mSelf)));
    }
    handle<> GetSelfHandle() const {
        return handle<>(borrowed(mSelf));
    }
    object GetSelfAttr(object name) const;
    object GetSelfAttr(char const* name) const;
    void SetSelfAttr(object name, object value) const;
    void SetSelfAttr(char const* name, object value) const;

    /// Return a shared_ptr<T> of self where T is CObject or a subclass.
    /// The shared_ptr will be managing a Python object.
    template <class T>
    TPtr<T> GetSelfPtr() const {
        T* p = dynamic_cast<T*>(const_cast<CObject*>(this));
        if (p)
            return TPtr<T>(p, handle<>(borrowed(mSelf)));
        else
            return TPtr<T>();
    }

    // equivalent to super(type, self)
    object GetSuper(object type) const;
    object GetSuper(py::type_info type) const;
    template <class T>
    object GetSuper() const {
        return GetSuper(type_id<T>());
    }
    template <class T>
    object GetSuperAttr(char const* name) const {
        return GetSuper(type_id<T>()).attr(name);
    }

    friend void _InitObjectBindings();
};

// Implements reference counting with intrusive_ptr for smaller objects
class KH_CORE_API FRefCounted {
    volatile long mRefCount;

public:
    FRefCounted(long InRefCount=0): mRefCount(InRefCount) {}
    virtual ~FRefCounted() {}

    friend void intrusive_ptr_add_ref(FRefCounted*);
    friend void intrusive_ptr_release(FRefCounted*);
};

inline void intrusive_ptr_add_ref(FRefCounted* r) {
    BOOST_INTERLOCKED_INCREMENT(&r->mRefCount);
}

inline void intrusive_ptr_release(FRefCounted* r) {
    if (BOOST_INTERLOCKED_DECREMENT(&r->mRefCount) == 0)
        delete r;
}

}} // namespace kharlia::core

ENABLE_BACK_REFERENCE(kharlia::core::CObject)

#endif